package com.runjian.demo.controller;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.http.HttpUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.BucketInfo;
import com.aliyun.oss.model.PutObjectResult;
import com.runjian.demo.base.Result;
import com.runjian.demo.config.OssBootConfiguration;
import com.runjian.demo.config.OssMeta;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

@Tag(name = "文件管理-接口")
@Slf4j
@RestController
@RequestMapping("/api/v1/oss")
public class FileUploadController {

    @Value("${aliyun.oss.staticDomain}")
    private String staticDomain;

    @Value("${aliyun.oss.callbackDomain}")
    private String callbackDomain;

    @Value("${aliyun.oss.sizeLimit}")
    private String sizeLimit;

    @Value("${aliyun.oss.roleArn}")
    private String roleArn;

    @Value("${aliyun.oss.bucketName}")
    private String bucketName;

    @Value("${aliyun.oss.endpoint}")
    private String endpoint;

    /**
     * 获取对象桶
     *
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/get/getBucketName", method = RequestMethod.GET)
    public Result<?> getBucketName(HttpServletRequest request) throws Exception {
        log.info("有请求到 [/upload/getBucketName] >>>>>>>>>>>");
        OSSClient client = OssBootConfiguration.ossClient();
        OssMeta ossMeta = OssBootConfiguration.ossMeta();
        String endpoint = ossMeta.getEndpoint(); // 请填写您的 endpoint。
        String accessKeyId = client.getCredentialsProvider().getCredentials().getAccessKeyId(); // 请填写您的AccessKeyId。
        String accessKeySecret = client.getCredentialsProvider().getCredentials().getSecretAccessKey();
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        String bucket = ossMeta.getBucketName(); // 请填写您的 bucketname 。
        String dir = "upload/" + DateUtil.formatDateTime(new Date()) + "/";
        String fileName = UUID.randomUUID().toString().replaceAll("-", ""); // 请填写您的 bucketname 。
        BucketInfo bucketInfo = ossClient.getBucketInfo(bucket);
        System.out.println("1" + "===>" + bucketInfo.getRequestId());
        return Result.success(bucketInfo.getBucket().getName());
    }

    /**
     * 上传文件
     *
     * @param file
     * @return
     * @throws Exception
     */
    @Operation(summary = "文件上传minio")
    @PostMapping(value = "/upload/fileImport")
    public Result<?> fileImport(@RequestParam(value = "file") MultipartFile file) throws Exception {
        log.info("有请求到 [/upload/fileImport] >>>>>>>>>>>");
        OSSClient client = OssBootConfiguration.ossClient();
        OssMeta ossMeta = OssBootConfiguration.ossMeta();
        String endpoint = ossMeta.getEndpoint(); // 请填写您的 endpoint。
        String accessKeyId = client.getCredentialsProvider().getCredentials().getAccessKeyId(); // 请填写您的AccessKeyId。
        String accessKeySecret = client.getCredentialsProvider().getCredentials().getSecretAccessKey();
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        String bucket = ossMeta.getBucketName(); // 请填写您的 bucketname 。
        String dir = "upload/" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_FORMATTER) + "/";
        String fileName = UUID.randomUUID().toString().replaceAll("-", ""); // 请填写您的 bucketname 。
        String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
        try {
            BucketInfo bucketInfo = ossClient.getBucketInfo(bucket);
            // 上传文件流
            PutObjectResult result = ossClient.putObject(bucket, dir + fileName + "." + suffix, file.getInputStream());
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS,  but was rejected with an error response for some reason.");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
            return Result.fail(500, oe.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return Result.success(staticDomain + "/" + dir + fileName + "." + suffix);
    }

    /**
     * H5上传回调接口
     *
     * @param request
     * @throws IOException
     */
    @PostMapping("/uploadCallback")
    public Map<String, String> uploadCallback(HttpServletRequest request) throws IOException {
        log.info("有请求到 [/uploadCallback] >>>>>>>>>>>");
        String ossCallbackBody = GetPostBody(request.getInputStream(),
                Integer.parseInt(request.getHeader("content-length")));
        boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody);
        Map<String, String> respMap = new LinkedHashMap<>();
        if (ret) {
            log.info("OSS Callback Verify success, Body:" + ossCallbackBody);
            respMap.put("Status", "OK");
        } else {
            log.info("OSS Callback Verify fail, Body:" + ossCallbackBody);
            respMap.put("Status", "verdify not ok");
        }
        return respMap;
    }


    /**
     * 获取Post消息体
     *
     * @param is
     * @param contentLen
     * @return
     */
    public String GetPostBody(InputStream is, int contentLen) {
        if (contentLen > 0) {
            int readLen = 0;
            int readLengthThisTime = 0;
            byte[] message = new byte[contentLen];
            try {
                while (readLen != contentLen) {
                    readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                    if (readLengthThisTime == -1) {// Should not happen.
                        break;
                    }
                    readLen += readLengthThisTime;
                }
                return new String(message);
            } catch (IOException e) {
            }
        }
        return "";
    }

    /**
     * 验证上传回调的Request
     *
     * @param request
     * @param ossCallbackBody
     * @return
     * @throws NumberFormatException
     * @throws IOException
     */
    protected boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody)
            throws NumberFormatException, IOException {
        boolean ret = false;
        String autorizationInput = new String(request.getHeader("Authorization"));
        String pubKeyInput = request.getHeader("x-oss-pub-key-url");

        byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
        byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
        String pubKeyAddr = new String(pubKey);
        if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/")
                && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
            System.out.println("pub key addr must be oss addrss");
            return false;
        }
        String retString = HttpUtil.get(pubKeyAddr);
        retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
        retString = retString.replace("-----END PUBLIC KEY-----", "");
        String queryString = request.getQueryString();
        String uri = request.getRequestURI();
        String decodeUri = URLDecoder.decode(uri, "UTF-8");
        String authStr = decodeUri;
        if (queryString != null && !queryString.equals("")) {
            authStr += "?" + queryString;
        }
        authStr += "\n" + ossCallbackBody;
        ret = doCheck(authStr, authorization, retString);
        return ret;
    }


    /**
     * 验证RSA
     *
     * @param content
     * @param sign
     * @param publicKey
     * @return
     */
    public static boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            boolean bverify = signature.verify(sign);
            return bverify;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

}
